
/* $Header: pi_mcalib.c,v 2.6 03/11/07 13:50:19 przybyls Exp $ */

/***********************************************************************/
/*                                                                     */
/*                  Copyright Hewlett-Packard Co. 1991                 */
/*                                                                     */
/***********************************************************************/

/***********************************************************************/
/* MODULE NAME: mcalib                                                 */
/* ============                                                        */
/*                                                                     */
/* Author: wikfors                                                     */
/*                                                                     */
/* MODULE PURPOSE: provide routines for doing mfg calibration.         */
/*                                                                     */
/***********************************************************************/


/*****************************/
/*   GLOBAL INCLUDE FILES    */
/*****************************/

#include <typedefs.h>
#include <error.h>
#include <p_wkfile.h>
#include <p_status.h>
#include <math.h>
#include <list_mgmt.h>
#include <pcb_defs.h>
#include <keyboard.h>

/*****************************/
/*   LOCAL INCLUDE FILES     */
/*****************************/

#include "proto.h"
#include "p_tcomp.h"

/*****************************/
/*   GLOBAL VARIABLES        */
/*****************************/


/*****************************/
/*   EXTERNAL VARIABLES      */
/*****************************/

/*****************************/
/*   LOCAL DEFINES           */
/*****************************/


/*****************************/
/*   STATIC VARIABLES        */
/*****************************/


/*****************************/
/*   IMPLEMENT SECTION       */
/*****************************/



#define MAX_RANK 4

/**begin_proto**/

float Determinate( float matrix[], INT16 rank )

/**end_proto**/
{

#define ODD(x)  ( 2*(int)(x/2) != x ? -1 : 1 )

     INT16  row, col, ndx;
     float   result;
     float   sub[ MAX_RANK * MAX_RANK ];

     if ( rank > MAX_RANK ) return 0;

     if ( rank == 1 ) return matrix [ 0 ];

     result = 0;

     /* cofactor method using first row as factors */

     for ( ndx = 0 ; ndx < rank ; ndx++ )                  /* each element (row = 0, column = ndx) of first row */
     {
         for ( row = 1 ; row < rank ; row++ )              /* all successive rows */
         {
             for ( col = 0 ; col < rank ; col++ )          /* all columns ...   */
             {
                 if ( col == ndx ) continue;                    /* except the one containing the current element */

                 sub[ (row-1)*(rank-1) + ((col<ndx)?col:(col-1)) ] = matrix [ row*rank + col ];

             }
         }

         result += ODD(ndx) * ( matrix [ ndx ] ) *  Determinate( sub, rank-1);
     }


     return result;
}



/**begin_proto**/

UI_ERR SaveThermisterConstants ( U_INT8 module_number, float v_25c, float dt, float dv )

/**end_proto**/
{
     BIT8 result;

     v_25c = v_25c * 13107;
     dv    = dv    * 13107;
     dt    = dt    * 64;

     if (( v_25c > 65535 ) || ( v_25c < 0      ))                return OVFLOW_V25C;
     if (( dt    > 32767 ) || ( dt    < -32768 ))                return OVFLOW_DT;
     if (( dv    > 32767 ) || ( dv    < -32768 ))                return OVFLOW_DV;
     if (( dv   == 0    ))                                       return ZERO_DV;

     result  = WriteEeprom( module_number, THERMISTOR_V_25C,       (U_INT16) v_25c );
     result &= WriteEeprom( module_number, THERMISTOR_dT,          (U_INT16) ((INT16)dt)    );
     result &= WriteEeprom( module_number, THERMISTOR_dV,          (U_INT16) ((INT16)dv)    );

     return (result) ? OK : EEPROM_WRITE_ERROR;
}


/**begin_proto**/

UI_ERR SavePsParms( U_INT8 module_number, U_INT8 position, U_INT16 range_iu, U_INT16 dcm2_per_iu,
                  float offset, float span, float doffset_dt, float dspan_dt )

/**end_proto**/
{
     BIT8 result;

     offset     = offset     * 13107;
     span       = span       * 13107;
     doffset_dt = doffset_dt * 13107 *  PS_dT_SCALING / 64;
     dspan_dt   = dspan_dt   * 13107 *  PS_dT_SCALING / 64;

     if (( offset     > 65535 ) || ( offset     < 0      ))                return OVFLOW_PS_OFFSET;
     if (( span       > 65535 ) || ( span       < 0      ))                return OVFLOW_PS_SPAN;
     if (( doffset_dt > 32767 ) || ( doffset_dt < -32768 ))                return OVFLOW_PS_DOFFSET;
     if (( dspan_dt   > 32767 ) || ( dspan_dt   < -32768 ))                return OVFLOW_PS_DSPAN;

     if ( range_iu    == 0 )                                               return ZERO_PS_RANGE;
     if ( dcm2_per_iu == 0 )                                               return ZERO_PS_DCM2;
     if ( offset      == 0 )                                               return ZERO_PS_OFFSET;
     if ( span        == 0 )                                               return ZERO_PS_SPAN;

     result  = WriteEeprom( module_number, PS_RANGE     +position-1,           range_iu             );
     result &= WriteEeprom( module_number, P_SCALING    +position-1,           dcm2_per_iu          );
     result &= WriteEeprom( module_number, PS_OFFSET    +position-1, (U_INT16) offset               );
     result &= WriteEeprom( module_number, PS_SPAN      +position-1, (U_INT16) span                 );
     result &= WriteEeprom( module_number, PS_DOFFSET_DT+position-1, (U_INT16) ((INT16)doffset_dt)  );
     result &= WriteEeprom( module_number, PS_DSPAN_DT  +position-1, (U_INT16) ((INT16)dspan_dt)    );

     return (result) ? OK : EEPROM_WRITE_ERROR;
}


UI_ERR SaveFsCalibParms( U_INT16 module, GAS_TYPE gas, U_INT16 *counts, float *temp, float ref_flow )
{

     U_INT16 type;
     BIT8    result;

     result = ReadEeprom( module, TYPE, &type );

     /* don't scribble jack in the box constants */
     if ( (U_INT16)type == ( ((U_INT16)INLET_MODULE<<8) | (U_INT16)JIB ) )  return OK;

     result  = WriteEeprom( module, GetInletParmAddress( gas, CALIB_REF_FLOW), (U_INT16)(ref_flow*100.0*273.15/298.15) );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_V0_T1 ), *(counts+0)  );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_V1_T1 ), *(counts+1)  );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_V3_T1 ), *(counts+2)  );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_V9_T1 ), *(counts+3)  );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_V0_T2 ), *(counts+4)  );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_V1_T2 ), *(counts+5)  );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_V3_T2 ), *(counts+6)  );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_V9_T2 ), *(counts+7)  );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_REF_TEMP_1 ),   (U_INT16)(*(temp)*100) );
     result &= WriteEeprom( module, GetInletParmAddress( gas, CALIB_REF_TEMP_2 ),   (U_INT16)(*(temp+1)*100) );

     return (result) ? OK : EEPROM_WRITE_ERROR;
}

UI_ERR ReadFsCalibValues( U_INT16 module, GAS_TYPE gas,
        U_INT16 *counts, float *temps, float *reference_flow, float * maximum_flow )
{

     U_INT16 temp[2];
     U_INT16 ref_flow;
     U_INT16 max_flow;
     U_INT16 type;
     BIT8    result;

     result = ReadEeprom( module, TYPE, &type );
     type = type & 0x0f;               /* remove the module type, just leave the inlet type */

     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_REF_FLOW), &ref_flow );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_V0_T1 ), (counts+0)  );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_V1_T1 ), (counts+1)  );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_V3_T1 ), (counts+2)  );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_V9_T1 ), (counts+3)  );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_V0_T2 ), (counts+4)  );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_V1_T2 ), (counts+5)  );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_V3_T2 ), (counts+6)  );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_V9_T2 ), (counts+7)  );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_REF_TEMP_1 ),   (temp+0) );
     result &= ReadEeprom( module, GetInletParmAddress( gas, CALIB_REF_TEMP_2 ),   (temp+1) );

     result &= ReadEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, MAX_INLET_FLOW ), &max_flow );


     *(temps+0) = ((float)temp[0]) / 100.0;
     *(temps+1) = ((float)temp[1]) / 100.0;

     *reference_flow = ref_flow / 100.0;
     *maximum_flow   = max_flow;

     return (result) ? OK : EEPROM_READ_ERROR;

}

#define MAX_ALPHA_LOW ( 32767.0 * FS_ALPHA_SCALING / 13107.0 )

/**begin_proto**/

UI_ERR SaveFsConstants(  U_INT16 module,    GAS_TYPE gas,      float max_flow,
                         float alpha_low,   float alpha_high, float beta_low, float beta_high, float offset,
                         float switch_flow, float da_dt,      float db_dt,    float dvo_dt,    float ref_temp )


/**end_proto**/
{

     U_INT16 type;
     BIT8 result;

     alpha_high = alpha_high * 13107 / FS_ALPHA_SCALING;       /* counts   */
     alpha_low  = alpha_low  * 13107 / FS_ALPHA_SCALING;       /* counts   */
     beta_high  = beta_high  * FS_BETA_SCALING;                /* unitless */
     beta_low   = beta_low   * FS_BETA_SCALING;                /* unitless */
     da_dt      = da_dt      * 13107 * FS_DALPHA_SCALING;      /* counts/C */
     db_dt      = db_dt      * FS_DBETA_SCALING  ;             /* unitless */
     dvo_dt     = dvo_dt     * 13107 * FS_DOFFSET_SCALING;     /* counts/C */
     offset     = offset     * 13107;                          /* counts   */
     ref_temp   = (ref_temp + 273.15) * 64;                    /* 64ths K  */


     if (( alpha_high > 65535 ) || ( alpha_high < 0      ))                return OVFLOW_FS_ALPHA_HIGH;
     if (( alpha_low  > 32767 ) || ( alpha_low  < -32768 ))                return OVFLOW_FS_ALPHA_LOW;
     if (( beta_high  > 65535 ) || ( beta_high  < 0      ))                return OVFLOW_FS_BETA_HIGH;
     if (( beta_low   > 32767 ) || ( beta_low   < -32768 ))                return OVFLOW_FS_BETA_LOW;
     if (( offset     > 65535 ) || ( offset     < 0      ))                return OVFLOW_FS_OFFSET;
     if (( ref_temp   > 65535 ) || ( ref_temp   < 0      ))                return OVFLOW_FS_REF_TEMP;
     if (( max_flow   > 65535 ) || ( max_flow   < 0      ))                return OVFLOW_FS_MAX_FLOW;
     if (( da_dt      > 32767 ) || ( da_dt      < -32768 ))                return OVFLOW_FS_DALPHA;
     if (( db_dt      > 32767 ) || ( db_dt      < -32768 ))                return OVFLOW_FS_DBETA;
     if (( dvo_dt     > 32767 ) || ( dvo_dt     < -32768 ))                return OVFLOW_FS_DOFFSET;

     if ( (( alpha_high == 0 ) || ( beta_high == 0 )) && ( switch_flow != 65535 )) return BAD_SWITCH_FLOW;

     if ( offset      == 0 )                                               return ZERO_FS_OFFSET;
     if ( alpha_low   == 0 )                                               return ZERO_FS_ALPHA_LOW;
     if ( beta_low    == 0 )                                               return ZERO_FS_BETA_LOW;


     result  = ReadModuleType( module, (U_INT16 *)&type );
     type = (type & 0xff);            /* remove the module type, just leave the inlet type */

     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, OFFSET      ), (U_INT16)         offset      );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, ALPHA_LOW   ), (U_INT16) ((INT16)alpha_low)  );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, ALPHA_HIGH  ), (U_INT16)         alpha_high  );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, BETA_LOW    ), (U_INT16) ((INT16)beta_low)   );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, BETA_HIGH   ), (U_INT16)         beta_high   );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, SWITCH_FLOW ), (U_INT16)         switch_flow );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, DALPHA      ), (U_INT16) ((INT16)da_dt)      );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, DBETA       ), (U_INT16) ((INT16)db_dt)      );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, DOFFSET     ), (U_INT16) ((INT16)dvo_dt)     );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, REF_TEMP    ), (U_INT16)         ref_temp    );
     result &= WriteEeprom( module, GetFSensorParmAddress( (INLET_TYPE)type, gas, MAX_INLET_FLOW),(U_INT16)        max_flow    );


     return (result) ? OK : EEPROM_WRITE_ERROR;
}


float CalcPSOffset( float range, float *pres, float *temp, float *volts )
{

    U_INT16 i;
    float matrix[ 16 ];
    float result, result2;

    /* use Cramers rule to solve the following system  for offset  */

    /*    volts1  =  offset + doffset_dt * t1 + (p1 / range) * ( span + dspan_dt * t1 ) */
    /*    volts2  =  offset + doffset_dt * t2 + (p2 / range) * ( span + dspan_dt * t2 ) */
    /*    volts3  =  offset + doffset_dt * t3 + (p3 / range) * ( span + dspan_dt * t3 ) */
    /*    volts4  =  offset + doffset_dt * t4 + (p4 / range) * ( span + dspan_dt * t4 ) */

    for ( i = 0 ; i < 4 ; i++ )
    {
         matrix[ (i*4)     ] = 1;                                  /* the offset coefficient */
         matrix[ (i*4) + 1 ] = *(temp+i);                          /* the do_dt  coefficient */
         matrix[ (i*4) + 2 ] = *(pres+i) / range;                  /* the span   coefficient */
         matrix[ (i*4) + 3 ] = (*(temp+i)) * (*(pres+i)) / range;  /* the ds_dt  coefficient */
    }

    result = Determinate( matrix, 4 );

    for ( i = 0 ; i < 4 ; i++ )
    {
         matrix[ (i*4)     ] = *(volts+i);  /* replace with the result */
    }

    result2 = Determinate( matrix, 4 );

    return  result2/result;

}


float CalcPSSpan  ( float range, float *pres, float *temp, float *volts )
{

    U_INT16 i;
    float matrix [ 16 ];
    float result;

    /* use Cramers rule to solve the following system  for span  */

    /*    volts1  =  offset + doffset_dt * t1 + (p1 / range) * ( span + dspan_dt * t1 ) */
    /*    volts2  =  offset + doffset_dt * t2 + (p2 / range) * ( span + dspan_dt * t2 ) */
    /*    volts3  =  offset + doffset_dt * t3 + (p3 / range) * ( span + dspan_dt * t3 ) */
    /*    volts4  =  offset + doffset_dt * t4 + (p4 / range) * ( span + dspan_dt * t4 ) */

    for ( i = 0 ; i < 4 ; i++ )
    {
         matrix [ (i*4)     ] = 1;                                  /* the offset coefficient */
         matrix [ (i*4) + 1 ] = *(temp+i);                          /* the do_dt  coefficient */
         matrix [ (i*4) + 2 ] = *(pres+i) / range;                  /* the span   coefficient */
         matrix [ (i*4) + 3 ] = (*(temp+i)) * (*(pres+i)) / range;  /* the ds_dt  coefficient */
    }

    result = Determinate( matrix ,4 );

    for ( i = 0 ; i < 4 ; i++ )
    {
         matrix [ (i*4) + 2 ] = *(volts+i);                         /* the span   coefficient */
    }

    return Determinate( matrix ,4 ) / result;

}


float CalcPSdOdT  ( float range, float *pres, float *temp, float *volts )
{

    U_INT16 i;
    float matrix[ 16 ];
    float result;

    /* use Cramers rule to solve the following system  for doffset_dt  */

    /*    volts1  =  offset + doffset_dt * t1 + (p1 / range) * ( span + dspan_dt * t1 ) */
    /*    volts2  =  offset + doffset_dt * t2 + (p2 / range) * ( span + dspan_dt * t2 ) */
    /*    volts3  =  offset + doffset_dt * t3 + (p3 / range) * ( span + dspan_dt * t3 ) */
    /*    volts4  =  offset + doffset_dt * t4 + (p4 / range) * ( span + dspan_dt * t4 ) */

    for ( i = 0 ; i < 4 ; i++ )
    {
         matrix[ (i*4)     ] = 1;                                  /* the offset coefficient */
         matrix[ (i*4) + 1 ] = *(temp+i);                          /* the do_dt  coefficient */
         matrix[ (i*4) + 2 ] = *(pres+i) / range;                  /* the span   coefficient */
         matrix[ (i*4) + 3 ] = (*(temp+i)) * (*(pres+i)) / range;  /* the ds_dt  coefficient */
    }

    result = Determinate( matrix, 4 );

    for ( i = 0 ; i < 4 ; i++ )
    {
         matrix[ (i*4) + 1 ] = *(volts+i);  /* replace with the result */
    }

    return Determinate(matrix,4)/result;

}



float CalcPSdSdT  ( float range, float *pres, float *temp, float *volts )
{

    U_INT16 i;
    float matrix[ 16 ];
    float result;

    /* use Cramers rule to solve the following system  for dspan_dt  */

    /*    volts1  =  offset + doffset_dt * t1 + (p1 / range) * ( span + dspan_dt * t1 ) */
    /*    volts2  =  offset + doffset_dt * t2 + (p2 / range) * ( span + dspan_dt * t2 ) */
    /*    volts3  =  offset + doffset_dt * t3 + (p3 / range) * ( span + dspan_dt * t3 ) */
    /*    volts4  =  offset + doffset_dt * t4 + (p4 / range) * ( span + dspan_dt * t4 ) */

    for ( i = 0 ; i < 4 ; i++ )
    {
         matrix[ (i*4)     ] = 1;                                  /* the offset coefficient */
         matrix[ (i*4) + 1 ] = *(temp+i);                          /* the do_dt  coefficient */
         matrix[ (i*4) + 2 ] = *(pres+i) / range;                  /* the span   coefficient */
         matrix[ (i*4) + 3 ] = (*(temp+i)) * (*(pres+i)) / range;  /* the ds_dt  coefficient */
    }

    result = Determinate( matrix, 4 );

    for ( i = 0 ; i < 4 ; i++ )
    {
         matrix[ (i*4) + 3 ] = *(volts+i);  /* replace with the result */
    }

    return Determinate(matrix,4)/result;

}



float CalcFSAlpha( float v1, float v3 )
{
    float part1, part2, part3;

    part1 = - sqrt( (4*v3 * pow( v1, 3 )) - (3 * pow( v1, 4 )) );
    part2 = 3 * pow( v1, 2 );
    part3 = 2 * ( v3 - (3 * v1) );

    return ( part1 - part2 ) / part3;


}


float CalcFSBeta( float flow, float alpha, float v1 )
{

     return log( alpha/(alpha-v1) ) / flow;


}

float CalcFsdAlpha( float alpha, float beta, float *temp,  float *dv, float flow )
{

    float  matrix [ 9 ];
    float  result;

    float  deltat;

       /* solve the eqns: */

       /* dV/dT = doffset/dt + [1-exp(-beta*flow)] dalpha/dt + alpha*flow*exp(-beta*flow)*dbeta/dt    flow = 0  */
       /* dV/dT = doffset/dt + [1-exp(-beta*flow)] dalpha/dt + alpha*flow*exp(-beta*flow)*dbeta/dt    flow = 1f */
       /* dV/dT = doffset/dt + [1-exp(-beta*flow)] dalpha/dt + alpha*flow*exp(-beta*flow)*dbeta/dt    flow = 3f */

    deltat = temp[0] - temp[1];

      /* f = 0 */
    matrix [ 0 ] =  ( dv[0] - dv[4] ) /  deltat;
    matrix [ 1 ] =  0;
    matrix [ 2 ] =  0;

      /* f = 1f */
    matrix [ 3 ] =  ( dv[0] - dv[4] ) / deltat;
    matrix [ 4 ] =  1 - exp ( - beta * flow );
    matrix [ 5 ] =  alpha * flow * exp( - beta * flow );

      /* f = 3f */
    matrix [ 6 ] =  ( dv[0] - dv[4] ) / deltat;
    matrix [ 7 ] =  1 - exp ( - beta * 3*flow );
    matrix [ 8 ] =  alpha * 3*flow * exp( - beta * 3*flow );


    result = Determinate( matrix, 3 );

    matrix [ 1 ] = ( dv[0] - dv[4] ) / deltat;
    matrix [ 4 ] = ( (dv[0]+dv[1]) - (dv[4]+dv[5]) ) / deltat;
    matrix [ 7 ] = ( (dv[0]+dv[2]) - (dv[4]+dv[6]) ) / deltat;

    return Determinate( matrix, 3 ) / result;

}



float CalcFsdBeta( float alpha, float beta, float *temp,  float *dv, float flow )
{

    float  matrix [ 9 ];
    float  result;

    float  deltat;

       /* solve the eqns: */

       /* dV/dT = doffset/dt + [1-exp(-beta*flow)] dalpha/dt + alpha*flow*exp(-beta*flow)*dbeta/dt    flow = 0  */
       /* dV/dT = doffset/dt + [1-exp(-beta*flow)] dalpha/dt + alpha*flow*exp(-beta*flow)*dbeta/dt    flow = 1f */
       /* dV/dT = doffset/dt + [1-exp(-beta*flow)] dalpha/dt + alpha*flow*exp(-beta*flow)*dbeta/dt    flow = 3f */

    deltat = temp[0] - temp[1];

      /* f = 0 */
    matrix [ 0 ] =  ( dv[0] - dv[4] ) /  deltat;
    matrix [ 1 ] =  0;
    matrix [ 2 ] =  0;

      /* f = 1f */
    matrix [ 3 ] =  ( dv[0] - dv[4] ) / deltat;
    matrix [ 4 ] =  1 - exp ( - beta * flow );
    matrix [ 5 ] =  alpha * flow * exp( - beta * flow );

      /* f = 3f */
    matrix [ 6 ] =  ( dv[0] - dv[4] ) / deltat;
    matrix [ 7 ] =  1 - exp ( - beta * 3*flow );
    matrix [ 8 ] =  alpha * 3*flow * exp( - beta * 3*flow );


    result = Determinate( matrix, 3 );

    matrix [ 2 ] = ( dv[0] - dv[4] ) / deltat;
    matrix [ 5 ] = ( (dv[0]+dv[1]) - (dv[4]+dv[5]) ) / deltat;
    matrix [ 8 ] = ( (dv[0]+dv[2]) - (dv[4]+dv[6]) ) / deltat;

    return Determinate( matrix, 3 ) / result;

}


static const float zero = 0.0;

/**begin_proto**/

UI_ERR DoAFlowSensor( U_INT16 module,     GAS_TYPE gas,      float ref_flow, float max_flow,   float *temp, U_INT16 *counts,
                      float *alpha_low,   float *alpha_high, float *beta_low, float *beta_high, float *offset,
                      float *switch_flow, float *da_dt,      float *db_dt,    float *dvo_dt,    float *ref_temp )


/**end_proto**/
{
     float dv[8];
     UI_ERR error;

     /* convert counts to delta volts */

     *(dv + 0)   = (float)*(counts + 0) / 13107;
     *(dv + 1)   = (float)*(counts + 1) / 13107 - dv[0];
     *(dv + 2)   = (float)*(counts + 2) / 13107 - dv[0];
     *(dv + 3)   = (float)*(counts + 3) / 13107 - dv[0];

     *(dv + 4)   = (float)*(counts + 4) / 13107;
     *(dv + 5)   = (float)*(counts + 5) / 13107 - dv[4];
     *(dv + 6)   = (float)*(counts + 6) / 13107 - dv[4];
     *(dv + 7)   = (float)*(counts + 7) / 13107 - dv[4];

     ref_flow = ref_flow * 298.15 / 273.15;      /* use NTP units */

     *alpha_low  = CalcFSAlpha ( dv[1], dv[2] );
     *beta_low   = CalcFSBeta  ( ref_flow,   *alpha_low,  dv[1] );

     if ( *(counts+3) != 0 )    /* handle case where we are only doing a single curve */
     {
         *alpha_high  = CalcFSAlpha ( dv[2], dv[3] );
         *beta_high   = CalcFSBeta  ( 3*ref_flow, *alpha_high, dv[2] );
         *switch_flow = 3*ref_flow;
     }
     else
     {
         *alpha_high  = 0;
         *beta_high   = 0;
         *switch_flow = 65535; /* converts to 0xffff */
     }


     *offset     = dv[0];
     *ref_temp   = temp[0];
     *dvo_dt     = ( dv[0] - dv[4] ) / ( temp[0] - temp[1] );

     if (( *(counts+5) != 0 ) &&
         ( *(counts+6) != 0 ))
     {
         *da_dt = CalcFsdAlpha ( *alpha_low, *beta_low, temp, dv, ref_flow );
         *db_dt = CalcFsdBeta  ( *alpha_low, *beta_low, temp, dv, ref_flow );
     }
     else
     {
         *da_dt = 0;
         *db_dt = 0;
     }

#if 0 /* real temp comp */
     if ((error=SaveFsConstants(  module,       gas,         max_flow,
                              *alpha_low,   *alpha_high, *beta_low, *beta_high, *offset,
                              *switch_flow, *da_dt,      *db_dt,    *dvo_dt,    *ref_temp )) == OK )
#else
     if ((error=SaveFsConstants(  module,       (GAS_TYPE)gas,         max_flow,
                              *alpha_low,   *alpha_high, *beta_low, *beta_high, *offset,
                              *switch_flow, zero,        zero,      zero,      *ref_temp )) == OK )
#endif
     {
         error = SaveFsCalibParms( module, (GAS_TYPE)gas, counts, temp, ref_flow );
     }

     return error;
}

/**begin_proto**/

UI_ERR DoAFlowSensorTry2( U_INT16 module, GAS_TYPE gas,      float ref_flow, float max_flow,   float *temp, U_INT16 *counts,
                      float *alpha_low,   float *alpha_high, float *beta_low, float *beta_high, float *offset,
                      float *switch_flow, float *da_dt,      float *db_dt,    float *dvo_dt,    float *ref_temp )


/**end_proto**/
{
     float dv[8];
     UI_ERR error;

     /* convert counts to delta volts */

     *(dv + 0)   = (float)*(counts + 0) / 13107;
     *(dv + 1)   = (float)*(counts + 1) / 13107 - dv[0];
     *(dv + 2)   = (float)*(counts + 2) / 13107 - dv[0];
     *(dv + 3)   = (float)*(counts + 3) / 13107 - dv[0];

     *(dv + 4)   = (float)*(counts + 4) / 13107;
     *(dv + 5)   = (float)*(counts + 5) / 13107 - dv[4];
     *(dv + 6)   = (float)*(counts + 6) / 13107 - dv[4];
     *(dv + 7)   = (float)*(counts + 7) / 13107 - dv[4];

     ref_flow = ref_flow * 298.15 / 273.15;      /* use NTP units */

     *alpha_low  = CalcFSAlpha ( dv[1], dv[2] );
     if ( *alpha_low < 0 ) *alpha_low = - MAX_ALPHA_LOW;    /* force alpha_low and recalculate a beta_low */
     else                  *alpha_low =   MAX_ALPHA_LOW;

     *beta_low   = CalcFSBeta  ( 3*ref_flow,   *alpha_low,  dv[2] );

     if ( *(counts+3) != 0 )    /* handle case where we are only doing a single curve */
     {
         *alpha_high  = CalcFSAlpha ( dv[2], dv[3] );
         *beta_high   = CalcFSBeta  ( 3*ref_flow, *alpha_high, dv[2] );
         *switch_flow = 3*ref_flow;
     }
     else
     {
         *alpha_high  = 0;
         *beta_high   = 0;
         *switch_flow = 65535; /* converts to 0xffff */
     }


     *offset     = dv[0];
     *ref_temp   = temp[0];
     *dvo_dt     = ( dv[0] - dv[4] ) / ( temp[0] - temp[1] );

     if (( *(counts+5) != 0 ) &&
         ( *(counts+6) != 0 ))
     {
         *da_dt = CalcFsdAlpha ( *alpha_low, *beta_low, temp, dv, ref_flow );
         *db_dt = CalcFsdBeta  ( *alpha_low, *beta_low, temp, dv, ref_flow );
     }
     else
     {
         *da_dt = 0;
         *db_dt = 0;
     }

#if 0 /* real temp comp */
     if ((error=SaveFsConstants(  module,   gas,         max_flow,
                              *alpha_low,   *alpha_high, *beta_low, *beta_high, *offset,
                              *switch_flow, *da_dt,      *db_dt,    *dvo_dt,    *ref_temp )) == OK )
#else
     if ((error=SaveFsConstants(  module,   gas,         max_flow,
                              *alpha_low,   *alpha_high, *beta_low, *beta_high, *offset,
                              *switch_flow, zero,        zero,      zero,      *ref_temp )) == OK )
#endif
     {
         error = SaveFsCalibParms( module, gas, counts, temp, ref_flow );
     }

     return error;
}



/**begin_proto**/

UI_ERR DoAFlowSensorTry3( U_INT16 module, GAS_TYPE gas,      float ref_flow, float max_flow,   float *temp, U_INT16 *counts,
                      float *alpha_low,   float *alpha_high, float *beta_low, float *beta_high, float *offset,
                      float *switch_flow, float *da_dt,      float *db_dt,    float *dvo_dt,    float *ref_temp )


/**end_proto**/
{
     float dv[8];
     UI_ERR error;

     /* convert counts to delta volts */

     *(dv + 0)   = (float)*(counts + 0) / 13107;
     *(dv + 1)   = (float)*(counts + 1) / 13107 - dv[0];
     *(dv + 2)   = (float)*(counts + 2) / 13107 - dv[0];
     *(dv + 3)   = (float)*(counts + 3) / 13107 - dv[0];

     *(dv + 4)   = (float)*(counts + 4) / 13107;
     *(dv + 5)   = (float)*(counts + 5) / 13107 - dv[4];
     *(dv + 6)   = (float)*(counts + 6) / 13107 - dv[4];
     *(dv + 7)   = (float)*(counts + 7) / 13107 - dv[4];

     ref_flow = ref_flow * 298.15 / 273.15;      /* use NTP units */

     if ( counts[3] != 0 )
     {
          *alpha_low  = CalcFSAlpha ( dv[2], dv[3] );
          *beta_low   = CalcFSBeta  ( 3*ref_flow, *alpha_high, dv[2] );

          *alpha_high  = 0;
          *beta_high   = 0;
          *switch_flow = 65535; /* converts to 0xffff */

          *offset     = dv[0];
          *ref_temp   = temp[0];
          *dvo_dt     = ( dv[0] - dv[4] ) / ( temp[0] - temp[1] );

          if (( *(counts+5) != 0 ) &&
              ( *(counts+6) != 0 ))
          {
              *da_dt = CalcFsdAlpha ( *alpha_low, *beta_low, temp, dv, ref_flow );
              *db_dt = CalcFsdBeta  ( *alpha_low, *beta_low, temp, dv, ref_flow );
          }
          else
          {
              *da_dt = 0;
              *db_dt = 0;
          }
     }
     else
     {
          *alpha_low  = CalcFSAlpha ( dv[1], dv[2] );
          *beta_low   = CalcFSBeta  ( ref_flow, *alpha_high, dv[1] );

          *alpha_high  = 0;
          *beta_high   = 0;
          *switch_flow = 65535; /* converts to 0xffff */

          *offset     = dv[0];
          *ref_temp   = temp[0];
          *dvo_dt     = ( dv[0] - dv[4] ) / ( temp[0] - temp[1] );

          if (( *(counts+5) != 0 ) &&
              ( *(counts+6) != 0 ))
          {
              *da_dt = CalcFsdAlpha ( *alpha_low, *beta_low, temp, dv, ref_flow );
              *db_dt = CalcFsdBeta  ( *alpha_low, *beta_low, temp, dv, ref_flow );
          }
          else
          {
              *da_dt = 0;
              *db_dt = 0;
          }
     }

#if 0 /* real temp comp */
     if ((error=SaveFsConstants(  module,   gas,         max_flow,
                              *alpha_low,   *alpha_high, *beta_low, *beta_high, *offset,
                              *switch_flow, *da_dt,      *db_dt,    *dvo_dt,    *ref_temp )) == OK )
#else
     if ((error=SaveFsConstants(  module,   gas,         max_flow,
                              *alpha_low,   *alpha_high, *beta_low, *beta_high, *offset,
                              *switch_flow, zero,        zero,      zero,      *ref_temp )) == OK )
#endif
     {
          error = SaveFsCalibParms( module, gas, counts, temp, ref_flow );
     }

     return error;
}

/**begin_proto**/

UI_ERR RecalibFlow( U_INT16 module, GAS_TYPE gas_type )

/**end_proto**/
{
   UI_ERR  error;

   float   ref_flow;
   float   max_flow;
   float   temp  [2];
   U_INT16 counts[8];

   float alpha_low, alpha_high, beta_low, beta_high, offset, switch_flow, da_dt, db_dt, dvo_dt, ref_temp;

   counts[0] =counts[1] =counts[2] =counts[3] =counts[4] =counts[5] =counts[6] =counts[7] = 0;
   alpha_low= alpha_high= beta_low= beta_high= offset= switch_flow= da_dt= db_dt= dvo_dt= ref_temp= 0;

   if ( (error = ReadFsCalibValues( module, gas_type, counts, temp, &ref_flow, &max_flow )) != OK ) return error;

   if (( counts[0] == 0 )  || ( counts[1] == 0 )  || ( counts[2] == 0 ) ||
       ( ref_flow == 0.0 ) || ( max_flow == 0.0 ) || ( temp[0] == 0.0 )) return INVALID_PARAM;

   error = OK;

   error = DoAFlowSensor( module, gas_type, ref_flow, max_flow, temp, counts, &alpha_low, &alpha_high,
                          &beta_low, &beta_high, &offset, &switch_flow, &da_dt, &db_dt, &dvo_dt, &ref_temp );

   if (( error == OVFLOW_FS_ALPHA_LOW ) ||
       ( error == OVFLOW_FS_BETA_LOW  ))
   {
        /* alpha low or beta low didn't work.  try forcing alpha to a max value and calc a beta */

        error = DoAFlowSensorTry2( module, gas_type, ref_flow, max_flow, temp, counts, &alpha_low, &alpha_high,
                               &beta_low, &beta_high, &offset, &switch_flow, &da_dt, &db_dt, &dvo_dt, &ref_temp );


        if (( error == OVFLOW_FS_ALPHA_LOW ) ||
            ( error == OVFLOW_FS_BETA_LOW  ) ||
            ( error == ZERO_FS_ALPHA_LOW   ) ||
            ( error == ZERO_FS_BETA_LOW    ) )
        {
             /* alpha low or beta low didn't work.  try using 3f, 9f and see if we get a good answer */

             error = DoAFlowSensorTry3( module, gas_type, ref_flow, max_flow, temp, counts, &alpha_low, &alpha_high,
                                    &beta_low, &beta_high, &offset, &switch_flow, &da_dt, &db_dt, &dvo_dt, &ref_temp );
        }
   }

   return error;

}


/***********************************************************************/
/* FUNCTION:                                                           */
/* ========                                                            */
/*                                                                     */
/* PURPOSE:                                                            */
/*                                                                     */
/* RETURNED VALUE:                                                     */
/*                                                                     */
/* GLOBALS USED:                                                       */
/*                                                                     */
/***********************************************************************/

extern U_INT16 AddToCrc( U_INT8 data, U_INT16 crc );


/**begin_proto**/

U_INT16  CalcCrcValue ( U_INT32 *data_words )

/**end_proto**/
{
    U_INT16 crc;
    U_INT8 i;

    crc = 0;

    for ( i = 0 ; i < 8 ; i ++ )
    {
        crc = AddToCrc ( (U_INT8) data_words [ i ] & 0xff000000 >> 24, crc );
        crc = AddToCrc ( (U_INT8) data_words [ i ] & 0x00ff0000 >> 16, crc );
        crc = AddToCrc ( (U_INT8) data_words [ i ] & 0x0000ff00 >>  8, crc );
        crc = AddToCrc ( (U_INT8) data_words [ i ] & 0x000000ff      , crc );
    }

    return crc;

}


/***********************************************************************/
/* FUNCTION:                                                           */
/* ========                                                            */
/*                                                                     */
/* PURPOSE:                                                            */
/*                                                                     */
/* RETURNED VALUE:                                                     */
/*                                                                     */
/* GLOBALS USED:                                                       */
/*                                                                     */
/***********************************************************************/

#define SPR_F_SCALE       1000
#define SPR_DFDP_SCALE 1000000


/**begin_proto**/

UI_ERR SavePurgeRegParms ( U_INT8 module, GAS_TYPE gas, float flow, float df_dp )

/**end_proto**/
{
     U_INT32 scaling;
     U_INT16 ps_scale;
     BIT8 result;

     ReadPurgeFlowScaling( module, &scaling );
     ReadPresScaling(      module, 1, &ps_scale );

     flow = flow * scaling;
     df_dp = df_dp * DF_DP_SCALE * scaling / (68947.57 / ps_scale );

     if (( flow  > 65535 ) || ( flow  < 0      ))                return OVFLOW_PURGE_FLOW;
     if (( df_dp > 32767 ) || ( df_dp < -32768 ))                return OVFLOW_DF_DP;

     if ( flow == 0 )                                            return ZERO_PURGE_FLOW;

     result = TRUE;

     switch ( gas )
     {
        case H2:
                   result &= WriteEeprom( module, H2_PURGE_FLOW, (U_INT16) flow  );
                   result &= WriteEeprom( module, H2_dPURGE_dP,  (U_INT16) ((INT16) df_dp) );
                   return (result) ? OK : EEPROM_WRITE_ERROR;


        case N2:
                   result &= WriteEeprom( module, N2_PURGE_FLOW, (U_INT16) flow  );
                   result &= WriteEeprom( module, N2_dPURGE_dP,  (U_INT16) ((INT16) df_dp) );
                   return (result) ? OK : EEPROM_WRITE_ERROR;

        case He:
                   result &= WriteEeprom( module, He_PURGE_FLOW, (U_INT16) flow  );
                   result &= WriteEeprom( module, He_dPURGE_dP,  (U_INT16) df_dp );
                   return (result) ? OK : EEPROM_WRITE_ERROR;

        case ArMe:
                   result &= WriteEeprom( module, ArMe_PURGE_FLOW, (U_INT16) flow  );
                   result &= WriteEeprom( module, ArMe_dPURGE_dP,  (U_INT16) ((INT16) df_dp) );
                   return (result) ? OK : EEPROM_WRITE_ERROR;

        default:   return INVALID_GAS;
     }

}

/**begin_proto**/

UI_ERR GetPurgeRegWords ( U_INT8 module, GAS_TYPE gas, INT32 *flow_word, INT32 *df_dp_word )

/**end_proto**/
{

   pINLET_STATUS status;
   U_INT32 flow;
   INT32   df_dp;
   UI_ERR  error;

   switch ( module )
   {
       case FRNT_INLET_MODULE_NUMBER: status = &Inst_Status.Pneu.Frnt_Inlet;
                                      break;
       case BACK_INLET_MODULE_NUMBER: status = &Inst_Status.Pneu.Back_Inlet;
                                      break;

       default: *flow_word = *df_dp_word = 0;
                return INAPPROPRIATE_MODULE;
   }

     if ((error = ReadPurgeRegComp ( status, gas, &flow, &df_dp )) != OK ) return error;

     *flow_word  = (INT32)((float)SPR_F_SCALE * flow / status->IU_Scaling);
     *df_dp_word = (INT32)((float)SPR_DFDP_SCALE * df_dp / ( DF_DP_SCALE * status->IU_Scaling / (68947.57 / status->Dcm2_Per_IU )));

     return OK;

}

/**begin_proto**/

UI_ERR  GetPurgeRegParms ( U_INT8 module, INT32 *data_words )

/**end_proto**/
{

    UI_ERR error;

    if ((error = GetPurgeRegWords ( module, N2,   &data_words[0], &data_words[1] )) != OK ) return error;
    if ((error = GetPurgeRegWords ( module, H2,   &data_words[2], &data_words[3] )) != OK ) return error;
    if ((error = GetPurgeRegWords ( module, He,   &data_words[4], &data_words[5] )) != OK ) return error;
    if ((error = GetPurgeRegWords ( module, ArMe, &data_words[6], &data_words[7] )) != OK ) return error;

    data_words[ 8 ] = CalcCrcValue ( (U_INT32 *)data_words );

    return OK;
}

/**begin_proto**/

UI_ERR  FieldPurgeRegUpdate ( U_INT8 module, INT32 *data_words )

/**end_proto**/
{

    BIT8 result;
    UI_ERR error;

    if (( module != FRNT_INLET_MODULE_NUMBER ) &&
        ( module != BACK_INLET_MODULE_NUMBER )) return NOT_ALLOWED;

    if ( data_words[ 8 ] != CalcCrcValue ( (U_INT32 *)data_words ) ) return INVALID_PARAM;

    result = TRUE;

    if ((error=SavePurgeRegParms( module,
                                  N2,
                                  (float)data_words[0] / SPR_F_SCALE,
                                  (float)data_words[1] / SPR_DFDP_SCALE )) != OK ) return error;

    if ((error=SavePurgeRegParms ( module,
                                   H2,
                                   (float)data_words[2] / SPR_F_SCALE,
                                   (float)data_words[3] / SPR_DFDP_SCALE )) != OK ) return error;

    if ((error=SavePurgeRegParms ( module,
                                   He,
                                   (float)data_words[4] / SPR_F_SCALE,
                                   (float)data_words[5] / SPR_DFDP_SCALE )) != OK ) return error;

    if ((error=SavePurgeRegParms ( module,
                                   ArMe,
                                   (float)data_words[6] / SPR_F_SCALE,
                                   (float)data_words[7] / SPR_DFDP_SCALE )) != OK ) return error;

   switch ( module )
   {
       case FRNT_INLET_MODULE_NUMBER: InitPurgeRegComp( &Inst_Status.Pneu.Frnt_Inlet );
                                      break;
       case BACK_INLET_MODULE_NUMBER: InitPurgeRegComp( &Inst_Status.Pneu.Back_Inlet );
                                      break;
       default:                       return INAPPROPRIATE_MODULE;
   }

    return result == TRUE ? OK : EEPROM_WRITE_ERROR;
}

